package hygprs

import (
	"encoding/hex"
	"hy_808/tcpx"
	"log"
	"math/rand"
	"runtime/debug"
	"time"
)

/*
配合tcpx包使用的函数
*/

// 包的最大长度
const MaxPackageLength = 3000

// 分割包
func Split(seg []byte, log *log.Logger) ([][]byte, []byte) {
	res := make([][]byte, 0)
	next := 0 // 正在检查的字符的位置
	last := 0 // 上个包的结束位置
	for next < len(seg) {
		// 找出可能的包头
		if h := seg[next]; h != FromBus && h != ToBus && h != FromDispatchServer && h != Internal {
			next++
			continue
		}
		// 剩余数据太短，读不到包头，退出循环
		if len(seg) < next+PositionData {
			break
		}
		// 获取包长度
		length := int(ToUint16(seg[next+PositionLength : next+PositionVersion]))
		// 包长度应该在大于等于12,小于最大包长，否则视为错误的包
		if length < 12 || length > MaxPackageLength {
			next++
			continue
		}
		// 剩余数据太短，读不到完整的包，退出循环
		end := next + length
		if len(seg) < end {
			break
		}
		// 初步确定包的范围，获取包数据进行检查
		data := seg[next:end]
		// 检查版本号
		if data[PositionVersion] != 0x01 && data[PositionVersion] != 0x02 && data[PositionVersion] != 0x03 {
			next++
			continue
		}
		// 检查包尾
		if data[length+PositionTail] != data[PositionHeader]+1 {
			next++
			continue
		}
		// 已确定包的范围，做进一步的处理
		// 如果包前面有无法解析的数据，记录并丢掉
		if next != last && log != nil {
			log.Println("跳过无法解析的数据,data:" + hex.EncodeToString(seg[last:next]))
		}
		// 检查校验和，如果正确则放入结果切片，不正确则记录并丢弃
		// 只检查协议版本为0x02的包
		if data[PositionVersion] != 0x02 || CheckSum(data[:length+PositionChecksum]) == ToUint32(data[length+PositionChecksum:length+PositionTail]) {
			res = append(res, data)
		} else if log != nil {
			log.Println("校验和错误，丢弃数据,data:" + hex.EncodeToString(data))
		}
		// 将偏移量移动到包尾，准备查找下一个包
		next = end
		last = end
	}
	// 当前数据已检查完，如果有无法解析的数据，记录并丢掉
	// 不要删掉这里，否则会导致问题数据积累到下个包
	if next != last && log != nil {
		log.Println("跳过无法解析的数据,data:" + hex.EncodeToString(seg[last:next]))
	}
	return res, seg[next:]
}

type ReceiveHandler struct {
	conn   *tcpx.Conn
	buf    []byte
	handle func(c *tcpx.Conn, p Receive) bool
}

/*
对接tcpx.MakeReceiveHandler的方法
当收到数据时，tcpx.Conn将数据交给本包的ReceiveHandler分包，然后将包交给传入的h函数处理
*/
func ReceiveProvider(h func(c *tcpx.Conn, p Receive) bool) tcpx.BuildReceiveHandler {
	return func(c *tcpx.Conn) tcpx.ReceiveHandler {
		return (&ReceiveHandler{
			conn:   c,
			handle: h,
		}).OnReceive
	}
}

func (h *ReceiveHandler) OnReceive(data []byte) bool {
	defer func() {
		if info := recover(); info != nil {
			h.conn.Error.Println("处理接收包时出现panic", info)
			log.Println(string(debug.Stack()))
		}
	}()
	var ps [][]byte
	ps, h.buf = Split(append(h.buf, data...), h.conn.Error)
	for _, packet := range ps {
		p := tcpx.Package{
			UniqueID:     rand.Int63(),
			ReceivedTime: time.Now(),
			Data:         packet,
		}
		if !h.handle(h.conn, MakeReceive(h.conn, p)) {
			return false
		}
	}
	return true
}
